﻿using System.Collections.Generic;
using System.Linq;
using PSE.Deployment.Engine.Enums;
using PSE.Deployment.Engine.Interfaces;
using PSE.Deployment.SetupBase.Configuration;
using PSE.Deployment.SetupBase.Parameter.Enums;
using PSE.Deployment.SetupBase.Steps;
using PSE.Deployment.Util;

namespace PSE.Deployment.SetupBase.Groups
{
    public class SetupInstall : SetupExecutionGroup
    {
        public SetupInstall(IStepGroup parent)
            : base(parent)
        {
            this.Id = "SetupInstall";
        }

        protected override void AddPreStepsExecution()
        {
            base.AddPreStepsExecution();

            SetupGroup group = new SetupGroup(this, new Group() { Id = "CheckInstalationSource", Description = "Verificação da origem dos dados" });
            group.CancelBehavior = StepGroupCancelBehavior.CancelParent;
            group.Steps.Add(new CheckInstalationSource(group));
            this.Steps.Add(group);
        }

        protected override void AddStepsExecution()
        {
            base.AddStepsExecution();

            IEnumerable<Group> orderedGroups = SortGroups(SetupMainFactory.GetInstance().Parameter.SetupConfiguration.SystemInfo.Groups);

            if (SetupMainFactory.GetInstance().Parameter.SetupMode == SetupMode.Install)
            {
                ExecuteGroupsInstall(orderedGroups);
            }
            else if (SetupMainFactory.GetInstance().Parameter.SetupMode == SetupMode.Uninstall)
            {
                ExecuteSubGroupsUninstall(orderedGroups);
                ExecuteGroupsDependenciesUninstall(orderedGroups, true);
            }
        }

        protected override void AddPosStepsExecution()
        {
            base.AddPosStepsExecution();

            SetupGroup group = new SetupGroup(this, new Group() { Id = "FinalizerGroup", Description = "Finalização da Instalação" });

            group.Steps.Add(new CopySetupFiles(group, group.Group,
                new Configuration.Actions.Copy() { Id = "CopySetupFiles", Description = "Copia dos arquivos do setup" }, SetupMainFactory.GetInstance().Parameter.SetupMode));

            group.Steps.Add(new SaveInstallationInformation(group, group.Group,
                new Configuration.Actions.UpdateWindowsRegistry() { Id = "SaveInstallationInformation", Description = "Salva informações de instalação" }, SetupMainFactory.GetInstance().Parameter.SetupMode));

            this.Steps.Add(group);
        }

        /// <summary>
        /// Adiciona os passos de intalação para todos os grupos do sistema
        /// </summary>
        /// <param name="groups">Todos os grupos do sistema</param>
        /// <param name="isDependency">Indica se o grupo é uma dependencia</param>
        private void ExecuteGroupsInstall(IEnumerable<Group> groups)
        {
            foreach (Group group in groups)
            {
                //Adiciona as dependencias
                ExecuteGroupsInstall(SortGroups(group.Dependencies));

                AddStep(group, Parameter.Enums.SetupMode.Install);

                //Adiciona os subgrupos
                ExecuteGroupsInstall(SortGroups(group.SubGroups));
            }
        }

        /// <summary>
        /// Adiciona os passos de desintalação somente os subgrupos do sistema
        /// </summary>
        /// <param name="groups">Todos os grupos do sistema</param>
        private void ExecuteSubGroupsUninstall(IEnumerable<Group> groups)
        {
            foreach (Group group in groups)
            {
                //Adiciona as subgrupos
                ExecuteSubGroupsUninstall(SortGroups(group.SubGroups));

                AddStep(group, Parameter.Enums.SetupMode.Uninstall);
            }
        }

        /// <summary>
        /// Adiciona os passos de desintalação somente os grupos que são dependencias
        /// </summary>
        /// <param name="groups">Todos os grupos do sistema</param>
        /// <param name="isDependency">Indica se o grupo é uma dependencia</param>
        private void ExecuteGroupsDependenciesUninstall(IEnumerable<Group> groups, bool addDependency)
        {
            foreach (Group group in groups)
            {
                //percorre os subgrupos
                ExecuteGroupsDependenciesUninstall(SortGroups(group.SubGroups), false);

                if (addDependency)
                    AddStep(group, Parameter.Enums.SetupMode.Uninstall);

                //Adiciona as dependencias
                ExecuteGroupsDependenciesUninstall(SortGroups(group.Dependencies), true);
            }
        }

        /// <summary>
        /// Adiciona o grupo num passo de instalação ou desinstalação
        /// </summary>
        /// <param name="group">Grupo</param>
        /// <param name="setupMode">Modo de instalação</param>
        private void AddStep(Group group, SetupMode setupMode)
        {
            if (setupMode == SetupMode.Install)
            {
                if ((group.Install == null || group.Install.GetValueOrDefault()) && !group.Installed)
                {
                    if (this.Steps.ToList().Find(s => s.Id == group.Id) == null)
                    {
                        SetupGroup setupGroup = new SetupGroup(this, group, setupMode);

                        this.Steps.Add(setupGroup);
                    }
                }
            }
            else if (setupMode == SetupMode.Uninstall)
            {
                if ((group.Install.HasValue && !group.Install.Value) && group.Installed)
                {
                    if (this.Steps.ToList().Find(s => s.Id == group.Id) == null)
                    {
                        SetupGroup setupGroup = new SetupGroup(this, group, setupMode);

                        this.Steps.Add(setupGroup);
                    }
                }
            }
        }

        /// <summary>
        /// Efetua a ordenação dos grupos
        /// </summary>
        /// <param name="groups">Todos os grupos do sistema</param>
        /// <returns>Grupos ordenados</returns>
        private IEnumerable<Group> SortGroups(IEnumerable<Group> groups)
        {
            IEnumerable<Group> orderedGroups = null;

            if (SetupMainFactory.GetInstance().Parameter.SetupMode == SetupMode.Install)
            {
                orderedGroups = groups.OrderBy(g => g.Index);
            }
            else if (SetupMainFactory.GetInstance().Parameter.SetupMode == SetupMode.Uninstall)
            {
                orderedGroups = groups.OrderByDescending(g => g.Index);
            }

            return orderedGroups;
        }
    }
}